import Dep, { popTarget, pushTarget } from "./dep";
import { queueWatcher } from "./schedular";
let wid = 0;
class Watcher{
    constructor(vm,exprOrFn,cb,options){
        this.vm = vm; // 这里先把正确的存好了，后面用的是this.vm
        this.cb = cb;
        this.options = options
        this.deps = [];
        this.depsId = new Set()
        this.id = wid++;
        this.lazy = !!options.lazy;  // lazy属性是用来标识默认是否调用函数
        this.dirty = this.lazy; // dirty属性是用来做缓存的

        this.user = !!options.user; // 如果是用户watcher会多一个属性 user:true
        // 如果给的是一个字符串， 需要去通过字符串取值
        if(typeof exprOrFn == 'function'){
            this.getter = exprOrFn
        }else{
            this.getter = function(){// 一调用函数就会去实例上取值
                let path = exprOrFn.split('.'); // [o,n]
                return path.reduce((vm,current)=>{
                    vm = vm[current]; //vm =  vm[o]  // / vm[o][n]
                    return vm; 
                },vm)
            }
        }
        // 以前的逻辑调用get的目的是为了渲染，但是现在调用get目的是？
        this.value = this.lazy ? void 0: this.get(); // 实现页面的渲染
    }
    get(){
        // todo ....
        pushTarget(this) // 默认情况下属性会将用户的watcher一并收集起来
        

        const value =  this.getter.call(this.vm); // 去实例中取值  触发getter
        popTarget()
        // 只有在渲染的时候才有Dep.target属性
        return value
    }
    addDep(dep){
        let id = dep.id;// 获取收集器的id 做去重操作
        if(!this.depsId.has(id)){
            this.deps.push(dep)
            this.depsId.add(id)
            dep.addWatcher(this)
        }
    }
    update(){
        if(this.lazy){ // 这个是计算属性
            this.dirty = true;
        }else{ // 渲染wacher 用户watcher
            queueWatcher(this);
        }
       
    }
    run(){
        // 稍后会触发run方法, 找到对应的回调让回调执行传入新值和老值
        let newValue = this.get(); // 获取最新的状态
        let oldValue = this.value; // 上次保留的老值
        this.value = newValue // 用新值作为老的值
        if(this.user){ // 这个是用户watcher
            this.cb.call(this.vm,newValue,oldValue);
        }
    }
    evaluate(){
        this.value = this.get();
        this.dirty = false;
    }
    depend(){
        let i = this.deps.length;
        while (i--){
            this.deps[i].depend(); // 让这两个dep记住这个渲染watcher
        }
    }
}



// 让属性记住对应的渲染函数，如果属性发生变化就调用对应的渲染函数

// 我们给每个属性增加一个收集器， 通过收集器来收集watcher

// 一个属性有一个dep -》 watcher？ 一个属性对应多个watcher
// 一个watcher 对应多少个dep？ 一个watcher对应多个dep

// 多对多

export default Watcher